//
// Created by 11067 on 2023/3/6.
//


#include "test.h"

#include <utility>

static const char *words[MAX_RESULT_SIZE] = {nullptr};
static char *result[MAX_RESULT_SIZE] = {nullptr};
static std::vector<std::string> res;
static std::vector<std::string> ans;
static std::string dir = "../test/test_graph/";

static int test_n(std::string file_in, std::string file_ans) {
    // read ans
    ans.clear();
    std::ifstream file_ans_in(file_ans);
    std::string line;
    std::getline(file_ans_in, line); // read num of chains
    while (std::getline(file_ans_in, line)) {
        trim(line);
        if (line.length() > 0) {
            ans.push_back(line);
        }
    }
    file_ans_in.close();
    // get my result
    res.clear();
    std::vector<std::string> words_list;
    file2wordlist(std::move(file_in), words_list);
    for (int i = 0; i < words_list.size(); i++) {
        words[i] = words_list[i].c_str();
    }
    int res_len = gen_chains_all(const_cast<char **>(words), (int) words_list.size(), result);
    for (int i = 0; i < res_len; i++) {
        std::string chain(result[i]);
        trim(chain);
        res.push_back(chain);
    }
    // compare
    int ret = 0;
    if (ans.size() != res.size()) {
        ret = -1;
    } else {
        std::sort(ans.begin(), ans.end());
        std::sort(res.begin(), res.end());
        for (int i = 0; i < ans.size(); i++) {
            if (ans[i] != res[i]) {
                ret = -2;
            }
        }
    }
    return ret;
}

static int test(std::string file_in, std::string file_ans,
                bool is_word, char h, char t, char j, bool has_cycle) {
    // read ans
    ans.clear();
    std::ifstream file_ans_in(file_ans);
    std::string line;
    while (std::getline(file_ans_in, line)) {
        trim(line);
        if (line.length() > 0) {
            ans.push_back(line);
        }
    }
    file_ans_in.close();
    // get my result
    res.clear();
    std::vector<std::string> word_list;
    file2wordlist(std::move(file_in), word_list);
    for (int i = 0; i < word_list.size(); i++) {
        words[i] = word_list[i].c_str();
    }
    int res_len;
    if (is_word) {
        res_len = gen_chain_word(const_cast<char **>(words), (int) word_list.size(), result,
                                 h, t, j, has_cycle);
    } else {
        res_len = gen_chain_char(const_cast<char **>(words), (int) word_list.size(), result,
                                 h, t, j, has_cycle);
    }
    for (int i = 0; i < res_len; i++) {
        res.emplace_back(result[i]);
    }
    // compare
    if (ans.empty() && res.empty()) {
        return 0;
    }
    // check conditions
    if (is_lower(h)) {
        if (res[0][0] != h) {
            return -1;
        }
    }
    if (is_lower(t)) {
        std::string tail = res[res.size() - 1];
        if (tail[tail.length() - 1] != t) {
            return -2;
        }
    }
    if (is_lower(j)) {
        for (auto s: res) {
            if (s[0] == j) {
                return -3;
            }
        }
    }
    // check chain
    std::set<std::string> word_set;
    word_set.insert(res[0]);
    for (int i = 1; i < res.size(); i++) {
        if (word_set.count(res[i]) != 0) {
            return -4;
        }
        word_set.insert(res[i]);
        if (res[i][0] != res[i - 1][res[i - 1].length() - 1]) {
            return -5;
        }
    }
    // compare_ans
    if (is_word) {
        if (ans.size() != res.size()) {
            return -6;
        }
    } else {
        int ans_cnt = 0, res_cnt = 0;
        for (const auto &s: ans) {
            ans_cnt += (int) s.length();
        }
        for (const auto &s: res) {
            res_cnt += (int) s.length();
        }
        if (ans_cnt != res_cnt) {
            return -7;
        }
    }
    return 0;
}

static int test_1() {
    std::string file = dir + "1_in.txt";
    std::string ans_file = dir + "1_ans-1.txt";
    return test_n(file, ans_file);
}

static int test_2() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-1.txt";
    return test(file, ans_file, true, '\0', '\0', '\0', false);
}

static int test_3() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-2.txt";
    return test(file, ans_file, false, '\0', '\0', '\0', false);
}

static int test_4() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-3.txt";
    return test(file, ans_file, true, 'b', '\0', '\0', false);
}

static int test_5() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-4.txt";
    return test(file, ans_file, true, '\0', 'k', '\0', false);
}

static int test_6() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-5.txt";
    return test(file, ans_file, true, '\0', '\0', 'f', false);
}

static int test_7() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-6.txt";
    return test(file, ans_file, true, 'e', 'c', '\0', false);
}

static int test_8() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-7.txt";
    return test(file, ans_file, true, 'e', '\0', 'g', false);
}

static int test_9() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-8.txt";
    return test(file, ans_file, true, '\0', 'c', 'f', false);
}

static int test_10() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-9.txt";
    return test(file, ans_file, true, 'e', 'd', 'g', false);
}

static int test_11() {
    std::string file = dir + "2_in.txt";
    std::string ans_file = dir + "2_ans-10.txt";
    return test(file, ans_file, true, '\0', 'k', 'i', false);
}

static int test_12() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-1.txt";
    return test(file, ans_file, true, '\0', '\0', '\0', true);
}

static int test_13() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-2.txt";
    return test(file, ans_file, false, '\0', '\0', '\0', true);
}

static int test_14() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-3.txt";
    return test(file, ans_file, true, 'c', '\0', '\0', true);
}

static int test_15() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-4.txt";
    return test(file, ans_file, true, '\0', 'h', '\0', true);
}

static int test_16() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-5.txt";
    return test(file, ans_file, true, '\0', '\0', 'c', true);
}

static int test_17() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-6.txt";
    return test(file, ans_file, true, 'b', 'c', '\0', true);
}

static int test_18() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-7.txt";
    return test(file, ans_file, true, 'a', '\0', 'b', true);
}

static int test_19() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-8.txt";
    return test(file, ans_file, true, '\0', 'b', 'c', true);
}

static int test_20() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-9.txt";
    return test(file, ans_file, true, 'd', 'b', 'c', true);
}

static int test_21() {
    std::string file = dir + "3_in.txt";
    std::string ans_file = dir + "3_ans-10.txt";
    return test(file, ans_file, true, 'g', 'f', 'f', true);
}

//static int test_22() {
//    std::string str;
//    std::vector<std::string> chain;
//    std::vector<std::vector<const std::string *>> chains;
//    str = "a ab bc c axxxxxxxxxc cd bd d ef fg";
//    Graph g = Graph(str);
//    g.has_cycle();
//    g.cnt_chains(chains);
//    for (int i = 0; i < 8; i++) {
//        char h = i == 7 ? '\0' : (char) (i + 'a');
//        for (int j = 0; j < 8; j++) {
//            char t = j == 7 ? '\0' : (char) (j + 'a');
//            for (int k = 0; k < 8; k++) {
//                char op_j = k == 7 ? '\0' : (char) (k + 'a');
//                g.longest_chain(h, t, op_j, true, chain);
//                g.longest_chain(h, t, op_j, false, chain);
//            }
//        }
//    }
//
//    return 0;
//}
//
//static int test_23() {
//    std::string str;
//    std::vector<std::string> chain;
//    std::vector<std::vector<const std::string *>> chains;
//    str = "axa a axb b bxb bxxxb bxc c cxc cxd d dxd axc axd cxa axd dxc dxb exf exg gxf fxg f fxf gxe";
//    Graph g = Graph(str);
//    g.has_cycle();
//    g.cnt_chains(chains);
//    std::vector<char> h_range({'\0', 'a', 'c', 'e'});
//    std::vector<char> t_range({'\0', 'a', 'c', 'e'});
//    std::vector<char> j_range({'\0', 'a', 'c', 'e'});
//    for (char h: h_range) {
//        for (char t: t_range) {
//            for (char j: j_range) {
//                g.longest_chain_r(h, t, j, true, chain);
//                g.longest_chain_r(h, t, j, false, chain);
//            }
//        }
//    }
//    return 0;
//}

TEST(FunctionTest, Graph) {
    std::set<std::string> set{"axb", "bxa"};
    Graph graph{set};
    ASSERT_TRUE(graph.has_cycle());
    ASSERT_TRUE(true);
    // graph_1
    ASSERT_EQ(test_1(), 0);
    // graph_2
    ASSERT_EQ(test_2(), 0);
    ASSERT_EQ(test_3(), 0);
    ASSERT_EQ(test_4(), 0);
    ASSERT_EQ(test_5(), 0);
    ASSERT_EQ(test_6(), 0);
    ASSERT_EQ(test_7(), 0);
    ASSERT_EQ(test_8(), 0);
    ASSERT_EQ(test_9(), 0);
    ASSERT_EQ(test_10(), 0);
    ASSERT_EQ(test_11(), 0);
    // graph_3
    ASSERT_EQ(test_12(), 0);
    ASSERT_EQ(test_13(), 0);
    ASSERT_EQ(test_14(), 0);
    ASSERT_EQ(test_15(), 0);
    ASSERT_EQ(test_16(), 0);
    ASSERT_EQ(test_17(), 0);
    ASSERT_EQ(test_18(), 0);
    ASSERT_EQ(test_19(), 0);
    ASSERT_EQ(test_20(), 0);
    ASSERT_EQ(test_21(), 0);
    // others
    //ASSERT_EQ(test_22(), 0);
    //ASSERT_EQ(test_23(), 0);
}

TEST(APITest, Alloc) {
    char **result1;
    result1 = alloc_result();
    result1[0] = new char[4]{'a', 'x', 'b', '\0'};
    free_result(result1, 1);
}

TEST(OptionParserTest, Test1) {
    Options options{};
    const char *args[]{"cli.exe", "-n", "1_in.txt"};
    OptionParser::parse(3, const_cast<char **>(args), options);
    Options expect{
            CountType::WordChainCount,
            '\0',
            '\0',
            '\0',
            false,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

TEST(OptionParserTest, Test2) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1_in.txt"};
    OptionParser::parse(3, const_cast<char **>(args), options);
    Options expect{
            CountType::MaxWordLengthWordChain,
            '\0',
            '\0',
            '\0',
            false,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

TEST(OptionParserTest, Test3) {
    Options options{};
    const char *args[]{"cli.exe", "-c", "1_in.txt"};
    OptionParser::parse(3, const_cast<char **>(args), options);
    Options expect{
            CountType::MaxAlphabetLengthWordChain,
            '\0',
            '\0',
            '\0',
            false,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

TEST(OptionParserTest, Test4) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1_in.txt", "-r"};
    OptionParser::parse(4, const_cast<char **>(args), options);
    Options expect{
            CountType::MaxWordLengthWordChain,
            '\0',
            '\0',
            '\0',
            true,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

TEST(OptionParserTest, Test5) {
    Options options{};
    const char *args[]{"cli.exe", "-r", "-w", "1_in.txt",};
    OptionParser::parse(4, const_cast<char **>(args), options);
    Options expect{
            CountType::MaxWordLengthWordChain,
            '\0',
            '\0',
            '\0',
            true,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

TEST(OptionParserTest, Test6) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1_in.txt", "-h", "a"};
    OptionParser::parse(5, const_cast<char **>(args), options);
    Options expect{
            CountType::MaxWordLengthWordChain,
            'a',
            '\0',
            '\0',
            false,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

TEST(OptionParserTest, Test7) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1_in.txt", "-t", "a"};
    OptionParser::parse(5, const_cast<char **>(args), options);
    Options expect{
            CountType::MaxWordLengthWordChain,
            '\0',
            'a',
            '\0',
            false,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

TEST(OptionParserTest, Test8) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1_in.txt", "-t", "a", "-h", "a"};
    OptionParser::parse(7, const_cast<char **>(args), options);
    Options expect{
            CountType::MaxWordLengthWordChain,
            'a',
            'a',
            '\0',
            false,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

TEST(OptionParserTest, Test9) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1_in.txt", "-j", "a"};
    OptionParser::parse(5, const_cast<char **>(args), options);
    Options expect{
            CountType::MaxWordLengthWordChain,
            '\0',
            '\0',
            'a',
            false,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

TEST(OptionParserTest, Test10) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1_in.txt", "-j", "a", "-r"};
    OptionParser::parse(6, const_cast<char **>(args), options);
    Options expect{
            CountType::MaxWordLengthWordChain,
            '\0',
            '\0',
            'a',
            true,
            "1_in.txt",
    };
    ASSERT_EQ(options, expect);
}

#define EXPECT_THROW_WITH_EXACT_MSG(f, t, msg) \
        try{                                    \
                f;                              \
                FAIL();                         \
        }catch(t& e) {                           \
           ASSERT_STREQ(msg,e.what());         \
           }

TEST(OptionParserTest, ErrTest1) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(3, const_cast<char **>(args), options),
                                CLIOptionException,
                                "file name invalid, should be txt.");
}

TEST(OptionParserTest, ErrTest2) {
    Options options{};
    const char *args[]{"cli.exe"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(1, const_cast<char **>(args), options),
                                CLIOptionException,
                                "too few options");
}

TEST(OptionParserTest, ErrTest3) {
    Options options{};
    const char *args[]{"cli.exe", "-gg", "233"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(3, const_cast<char **>(args), options),
                                CLIOptionException,
                                "invalid option");
}

TEST(OptionParserTest, ErrTest4) {
    Options options{};
    const char *args[]{"cli.exe", "-n", "1.txt", "-n", "2.txt"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "file name has been specified more than once");
}

TEST(OptionParserTest, ErrTest5) {
    Options options{};
    const char *args[]{"cli.exe", "-n", "1.jpg"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(3, const_cast<char **>(args), options),
                                CLIOptionException,
                                "file name invalid, should be txt.");
}

TEST(OptionParserTest, ErrTest6) {
    Options options{};
    const char *args[]{"cli.exe", "-c", "1.txt", "-c", "2.txt"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "file name has been specified more than once");
}

TEST(OptionParserTest, ErrTest7) {
    Options options{};
    const char *args[]{"cli.exe", "-c", "1.jpg"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(3, const_cast<char **>(args), options),
                                CLIOptionException,
                                "file name invalid, should be txt.");
}

TEST(OptionParserTest, ErrTest8) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-w", "2.txt"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "file name has been specified more than once");
}

TEST(OptionParserTest, ErrTest9) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.jpg"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(3, const_cast<char **>(args), options),
                                CLIOptionException,
                                "file name invalid, should be txt.");
}

TEST(OptionParserTest, ErrTest10) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-c", "2.txt"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "file name has been specified more than once");
}

TEST(OptionParserTest, ErrTest11) {
    Options options{};
    const char *args[]{"cli.exe", "-n", "1.txt", "-r"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(4, const_cast<char **>(args), options),
                                CLIOptionException,
                                "-n should not be used with other options.");
}

TEST(OptionParserTest, ErrTest12) {
    Options options{};
    const char *args[]{"cli.exe", "-j", "a", "-t", "a", "-r"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(6, const_cast<char **>(args), options),
                                CLIOptionException,
                                "no file specified.");
}

TEST(OptionParserTest, ErrTest13) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-h", "aa"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "should specify only ONE character for -h.");
}

TEST(OptionParserTest, ErrTest14) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-t", "aa"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "should specify only ONE character for -t.");
}

TEST(OptionParserTest, ErrTest15) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-j", "aa"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "should specify only ONE character for -j.");
}

TEST(OptionParserTest, ErrTest16) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-j", "a", "-j", "a"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(7, const_cast<char **>(args), options),
                                CLIOptionException,
                                "-j option has been specified more than once.");
}

TEST(OptionParserTest, ErrTest17) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-h", "a", "-h", "a"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(7, const_cast<char **>(args), options),
                                CLIOptionException,
                                "-h option has been specified more than once.");
}

TEST(OptionParserTest, ErrTest18) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-t", "a", "-t", "a"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(7, const_cast<char **>(args), options),
                                CLIOptionException,
                                "-t option has been specified more than once.");
}

TEST(OptionParserTest, ErrTest19) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-r", "-r"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "-r option has been specified more than once.");
}

TEST(OptionParserTest, ErrTest20) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-g", "c"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "option not recognized.");
}

TEST(OptionParserTest, ErrTest21) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-h", ";"};
    EXPECT_THROW_WITH_EXACT_MSG(OptionParser::parse(5, const_cast<char **>(args), options),
                                CLIOptionException,
                                "only [a-zA-Z] is allowed for option.");
}

TEST(OptionParserTest, Test11) {
    Options options{};
    const char *args[]{"cli.exe", "-w", "1.txt", "-h", "H"};
    OptionParser::parse(4, const_cast<char **>(args), options);
    ASSERT_EQ(options.beginWith, 'h');
}
static int splitStr(std::string &str, char **&pString) {
    const char *cStr = str.c_str();
    auto pWordBegin{cStr};
    size_t wordLength{0};
    std::vector<char *> words_vec;
    while (pWordBegin[wordLength] != '\0') {
        if (is_alpha(pWordBegin[wordLength])) {
            wordLength++;
        } else  {
            if (wordLength > 0) {
                char *word{new char[wordLength + 1]};
                strncpy_s(word, wordLength + 1, pWordBegin, wordLength);
                word[wordLength] = '\0';
                words_vec.push_back(word);
                pWordBegin += wordLength;
                wordLength = 0;
            } else {
                pWordBegin += 1;
            }
        }
    }
    if (wordLength > 0) {
        char *word{new char[wordLength + 1]};
        strncpy_s(word, wordLength + 1, pWordBegin, wordLength);
        word[wordLength] = '\0';
        words_vec.push_back(word);
    }
    // 有没有办法把内存`move out` vector?
    pString = new char *[words_vec.size()];
    for (int i = 0; i < words_vec.size(); ++i) {
        pString[i] = words_vec[i];
    }
    return static_cast<int>(words_vec.size());
}
TEST(APITest, ErrTest1) {
    const char *words1[]{"axb", "bxa"};
//    char **words1;
//    std::string w{"axb bxa"};
//    splitStr(w, words1);
    char **result1 = alloc_result();
    EXPECT_THROW_WITH_EXACT_MSG(gen_chains_all(const_cast<char **>(words1), 2, result1),
                                APIException,
                                "word list has cycle without specify `enable_loop(-r)`(-n not support).");
    free_result(result1, 0);
}

TEST(APITest, ErrTest2) {
    char **words1{new char *[10000]};
    int cnt = 0;
    //TODO: 300多的数据规模核心算法就炸了 has_cycle? maybe
    for (int i = 0; i < 26; ++i) {
        for (int j = i + 1; j < 26; ++j) {
            for (int k = 0; k < 1; ++k,++cnt) {
                char *t = new char[4];
                t[3] = '\0';
                t[0] = 'a' + i;
                t[1] = 'a' + k;
                t[2] = 'a' + j;
                words1[cnt] = t;
            }
        }
    }
    char ** result1 = alloc_result();
    int r;
    EXPECT_THROW_WITH_EXACT_MSG(r=gen_chains_all(words1, cnt, result1),
                                APIException,
                                "result too long.");
    free_result(result1,0);
    for (int i = 0; i < cnt; ++i) {
        delete[] words1[i];
    }
    delete[] words1;
}

TEST(APITest, ErrTest3) {
    const char *words1[]{"axb", "bxa"};
    char **result1 = alloc_result();
    EXPECT_THROW_WITH_EXACT_MSG(gen_chain_word(const_cast<char **>(words1), 2, result1,
                                                   '\0', '\0', '\0', false),
                                APIException,
                                "word list has cycle without specify `enable_loop(-r)`.");
    free_result(result1, 0);
}

TEST(APITest, ErrTest4) {
    const char *words1[]{"axb", "bxa"};
    char **result1 = alloc_result();
    EXPECT_THROW_WITH_EXACT_MSG(gen_chain_char(const_cast<char **>(words1), 2, result1,
                                                   '\0', '\0', '\0', false),
                                APIException,
                                "word list has cycle without specify `enable_loop(-r)`.");
    free_result(result1, 0);
}

TEST(FunctionTest, Err1) {
    const char* words[] {"axa","aya"};
    char **result1;
    result1 = alloc_result();
    EXPECT_THROW_WITH_EXACT_MSG(gen_chains_all(const_cast<char**>(words), 2, result1), APIException,
                                "word list has cycle without specify `enable_loop(-r)`(-n not support).");
    free_result(result1, 0);

}

TEST(FunctionTest, Test1) {
    const char* words[] {"axb", "bxc", "cxc"};
    char **result1;
    result1 = alloc_result();
    std::vector<std::string> ans{"axb bxc ", "bxc cxc ", "axb bxc cxc "};
    int r = gen_chains_all(const_cast<char**>(words), 3, result1);
    std::vector<std::string> res_vec;
    for (int i = 0; i < r; ++i) {
        res_vec.emplace_back(result1[i]);
    }
    ASSERT_THAT(res_vec, testing::UnorderedElementsAreArray(ans));
    free_result(result1, r);
}

TEST(FileTest, Test1) {
    //const char *file = "notExist.txt";
    const char *args[]{"cli.exe", "-w", "notExist.txt"};
    EXPECT_THROW_WITH_EXACT_MSG(myMain(3, const_cast<char **>(args)), FileException, "failed to open file:notExist.txt");
}